<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head th:replace="~{fragments/layout :: head('熔断器测试')}"></head>
<body>
    <!-- 导航栏 -->
    <nav th:replace="~{fragments/layout :: navbar}"></nav>
    
    <!-- 页面内容 -->
    <div th:replace="~{fragments/layout :: content(~{::main})}">
        <main>
            <!-- 标题 -->
            <div class="container mt-4">
                <h1 class="mb-4">熔断器与服务降级测试</h1>
                <p class="lead">本页面用于测试Spring Cloud CircuitBreaker熔断器功能，结合了超时控制和重试机制实现服务的降级和熔断。</p>
                <hr>
                
                <!-- 测试卡片 -->
                <div class="row">
                    <div class="col-md-6">
                        <div class="card mb-4">
                            <div class="card-header bg-primary text-white">
                                <h5 class="mb-0">熔断器调用测试</h5>
                            </div>
                            <div class="card-body">
                                <p>配置熔断器调用选项：</p>
                                
                                <!-- 超时和重试选项 -->
                                <div class="mb-3">
                                    <div class="form-check form-switch mb-2">
                                        <input class="form-check-input" type="checkbox" id="timeoutSwitch" th:checked="${timeout}">
                                        <label class="form-check-label" for="timeoutSwitch">模拟超时（等待15秒）</label>
                                    </div>
                                    <div class="form-check form-switch">
                                        <input class="form-check-input" type="checkbox" id="retrySwitch" th:checked="${retry}">
                                        <label class="form-check-label" for="retrySwitch">启用重试机制</label>
                                    </div>
                                </div>
                                
                                <!-- 熔断器模式 -->
                                <div class="mb-3">
                                    <label class="form-label">熔断器模式：</label>
                                    <div class="form-check">
                                        <input class="form-check-input" type="radio" name="modeRadio" id="modeBasic" value="basic" th:checked="${mode == 'basic'}">
                                        <label class="form-check-label" for="modeBasic">
                                            基本熔断器
                                        </label>
                                    </div>
                                    <div class="form-check">
                                        <input class="form-check-input" type="radio" name="modeRadio" id="modeTimeout" value="timeout" th:checked="${mode == 'timeout'}">
                                        <label class="form-check-label" for="modeTimeout">
                                            超时限制熔断器
                                        </label>
                                    </div>
                                    <div class="form-check">
                                        <input class="form-check-input" type="radio" name="modeRadio" id="modeRetry" value="retry" th:checked="${mode == 'retry'}">
                                        <label class="form-check-label" for="modeRetry">
                                            重试机制熔断器
                                        </label>
                                    </div>
                                </div>
                                
                                <button id="testCircuitBreakerBtn" class="btn btn-primary">测试熔断器调用</button>
                                <div class="mt-3">
                                    <div class="d-flex align-items-center mb-2">
                                        <span>调用状态：</span>
                                        <span id="callStatus" class="ms-2 badge bg-secondary">未调用</span>
                                    </div>
                                    <div class="d-flex align-items-center">
                                        <span>调用次数：</span>
                                        <span id="callCount" class="ms-2">0</span>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                    
                    <div class="col-md-6">
                        <div class="card mb-4">
                            <div class="card-header bg-info text-white">
                                <h5 class="mb-0">自动测试</h5>
                            </div>
                            <div class="card-body">
                                <p>启动自动测试，每隔2秒调用一次服务，用于触发熔断器：</p>
                                <div class="d-flex">
                                    <button id="startAutoTestBtn" class="btn btn-success me-2">开始自动测试</button>
                                    <button id="stopAutoTestBtn" class="btn btn-danger" disabled>停止自动测试</button>
                                </div>
                                <div class="mt-3">
                                    <div class="d-flex align-items-center">
                                        <span>自动测试状态：</span>
                                        <span id="autoTestStatus" class="ms-2 badge bg-secondary">未启动</span>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                
                <!-- 结果展示 -->
                <div class="card mb-4">
                    <div class="card-header bg-dark text-white">
                        <h5 class="mb-0">调用结果</h5>
                    </div>
                    <div class="card-body">
                        <div class="mb-3">
                            <h6>当前服务信息：</h6>
                            <pre id="currentServiceInfo" class="bg-light p-3 rounded">未获取</pre>
                        </div>
                        <div>
                            <h6>熔断器调用结果：</h6>
                            <pre id="circuitBreakerResult" class="bg-light p-3 rounded">未调用</pre>
                        </div>
                    </div>
                </div>
                
                <!-- 调用历史 -->
                <div class="card mb-4">
                    <div class="card-header bg-success text-white">
                        <h5 class="mb-0">调用历史</h5>
                    </div>
                    <div class="card-body">
                        <div class="table-responsive">
                            <table class="table table-striped table-hover">
                                <thead>
                                    <tr>
                                        <th>#</th>
                                        <th>时间</th>
                                        <th>当前端口</th>
                                        <th>模式</th>
                                        <th>超时</th>
                                        <th>重试</th>
                                        <th>状态</th>
                                    </tr>
                                </thead>
                                <tbody id="historyTableBody">
                                    <!-- 历史记录将通过JavaScript添加 -->
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>
                
                <!-- 熔断器状态 -->
                <div class="card mb-4">
                    <div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
                        <h5 class="mb-0">熔断器状态监控</h5>
                        <button id="refreshStatusBtn" class="btn btn-sm btn-light">刷新状态</button>
                    </div>
                    <div class="card-body">
                        <div class="mb-3">
                            <h6>serviceInfoCircuitBreaker 熔断器状态：</h6>
                            <div class="d-flex align-items-center mb-2">
                                <span>当前状态：</span>
                                <span id="circuitBreakerState" class="ms-2 badge bg-secondary">未知</span>
                            </div>
                            <div class="row">
                                <div class="col-md-6">
                                    <ul class="list-group">
                                        <li class="list-group-item d-flex justify-content-between align-items-center">
                                            失败率
                                            <span id="failureRate" class="badge bg-primary rounded-pill">0%</span>
                                        </li>
                                        <li class="list-group-item d-flex justify-content-between align-items-center">
                                            慢调用率
                                            <span id="slowCallRate" class="badge bg-primary rounded-pill">0%</span>
                                        </li>
                                    </ul>
                                </div>
                                <div class="col-md-6">
                                    <ul class="list-group">
                                        <li class="list-group-item d-flex justify-content-between align-items-center">
                                            成功调用次数
                                            <span id="successfulCalls" class="badge bg-success rounded-pill">0</span>
                                        </li>
                                        <li class="list-group-item d-flex justify-content-between align-items-center">
                                            失败调用次数
                                            <span id="failedCalls" class="badge bg-danger rounded-pill">0</span>
                                        </li>
                                        <li class="list-group-item d-flex justify-content-between align-items-center">
                                            慢调用次数
                                            <span id="slowCalls" class="badge bg-warning rounded-pill">0</span>
                                        </li>
                                        <li class="list-group-item d-flex justify-content-between align-items-center">
                                            被拒绝调用次数
                                            <span id="notPermittedCalls" class="badge bg-secondary rounded-pill">0</span>
                                        </li>
                                    </ul>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                
                <!-- 技术说明 -->
                <div class="card mb-4">
                    <div class="card-header bg-secondary text-white">
                        <h5 class="mb-0">熔断器技术说明</h5>
                    </div>
                    <div class="card-body">
                        <p>熔断器是微服务架构中的一种重要模式，用于防止服务级联失败。主要特点：</p>
                        <ul>
                            <li>当服务调用失败率达到阈值时，熔断器会打开，快速失败而不是等待超时</li>
                            <li>熔断器打开后，会触发降级逻辑，返回备用结果</li>
                            <li>一段时间后，熔断器会进入半开状态，允许少量请求通过</li>
                            <li>如果这些请求成功，熔断器会关闭；如果失败，继续保持打开状态</li>
                        </ul>
                        <p>本示例使用Spring Cloud CircuitBreaker和Resilience4j实现熔断功能：</p>
                        <ul>
                            <li>基本熔断器：使用@CircuitBreaker注解实现基本的熔断功能</li>
                            <li>超时限制：使用@TimeLimiter注解实现超时控制，超时后触发降级</li>
                            <li>重试机制：使用@Retry注解实现重试功能，多次重试失败后触发降级</li>
                        </ul>
                        <p>熔断器配置：</p>
                        <ul>
                            <li>失败率阈值：40%（超过40%的请求失败时触发熔断）</li>
                            <li>熔断器打开状态持续时间：5秒</li>
                            <li>滑动窗口大小：基于最近5次调用的统计</li>
                            <li>半开状态允许的调用次数：2次</li>
                        </ul>
                    </div>
                </div>
            </div>
        </main>
    </div>
    
    <!-- 页脚 -->
    <footer th:replace="~{fragments/layout :: footer}"></footer>
    
    <!-- 脚本 -->
    <div th:replace="~{fragments/layout :: scripts}"></div>
    
    <!-- 熔断器测试脚本 -->
    <script th:inline="javascript">
        document.addEventListener('DOMContentLoaded', function() {
            // 计数器
            let callCount = 0;
            let autoTestInterval = null;
            
            // 获取DOM元素
            const testCircuitBreakerBtn = document.getElementById('testCircuitBreakerBtn');
            const startAutoTestBtn = document.getElementById('startAutoTestBtn');
            const stopAutoTestBtn = document.getElementById('stopAutoTestBtn');
            const callStatusEl = document.getElementById('callStatus');
            const callCountEl = document.getElementById('callCount');
            const autoTestStatusEl = document.getElementById('autoTestStatus');
            const currentServiceInfoEl = document.getElementById('currentServiceInfo');
            const circuitBreakerResultEl = document.getElementById('circuitBreakerResult');
            const historyTableBody = document.getElementById('historyTableBody');
            
            // 获取选项元素
            const timeoutSwitch = document.getElementById('timeoutSwitch');
            const retrySwitch = document.getElementById('retrySwitch');
            const modeRadios = document.querySelectorAll('input[name="modeRadio"]');
            
            // 获取选中的模式
            function getSelectedMode() {
                for (const radio of modeRadios) {
                    if (radio.checked) {
                        return radio.value;
                    }
                }
                return 'basic'; // 默认模式
            }
            
            // 测试熔断器调用
            function testCircuitBreaker() {
                callStatusEl.textContent = '调用中...';
                callStatusEl.className = 'ms-2 badge bg-warning';
                
                // 获取配置
                const timeout = timeoutSwitch.checked;
                const retry = retrySwitch.checked;
                const mode = getSelectedMode();
                
                fetch(`/api/circuit-breaker?timeout=${timeout}&retry=${retry}&mode=${mode}`)
                    .then(response => response.json())
                    .then(data => {
                        // 更新计数器
                        callCount++;
                        callCountEl.textContent = callCount;
                        
                        // 更新调用状态
                        if (data.status === 'success') {
                            callStatusEl.textContent = '调用成功';
                            callStatusEl.className = 'ms-2 badge bg-success';
                        } else {
                            callStatusEl.textContent = '调用失败';
                            callStatusEl.className = 'ms-2 badge bg-danger';
                        }
                        
                        // 更新结果展示
                        currentServiceInfoEl.textContent = JSON.stringify({
                            currentPort: data.currentPort,
                            message: data.message,
                            mode: data.mode
                        }, null, 2);
                        
                        circuitBreakerResultEl.textContent = JSON.stringify(data.circuitBreakerResult, null, 2);
                        
                        // 添加到历史记录
                        addToHistory({
                            currentPort: data.currentPort,
                            mode: data.mode,
                            timeout: timeout,
                            retry: retry,
                            status: data.status
                        });
                    })
                    .catch(error => {
                        console.error('调用失败:', error);
                        callStatusEl.textContent = '调用异常';
                        callStatusEl.className = 'ms-2 badge bg-danger';
                        
                        // 更新结果展示
                        circuitBreakerResultEl.textContent = JSON.stringify({
                            error: '调用异常: ' + error.message
                        }, null, 2);
                    });
            }
            
            // 添加到历史记录
            function addToHistory(data) {
                const now = new Date();
                const timeString = now.toLocaleTimeString();
                
                const row = document.createElement('tr');
                row.innerHTML = `
                    <td>${callCount}</td>
                    <td>${timeString}</td>
                    <td>${data.currentPort}</td>
                    <td>${data.mode}</td>
                    <td>${data.timeout ? '是' : '否'}</td>
                    <td>${data.retry ? '是' : '否'}</td>
                    <td><span class="badge ${data.status === 'success' ? 'bg-success' : 'bg-danger'}">${data.status === 'success' ? '成功' : '失败'}</span></td>
                `;
                
                historyTableBody.prepend(row);
                
                // 限制历史记录数量
                if (historyTableBody.children.length > 20) {
                    historyTableBody.removeChild(historyTableBody.lastChild);
                }
            }
            
            // 开始自动测试
            function startAutoTest() {
                if (autoTestInterval) {
                    return;
                }
                
                autoTestStatusEl.textContent = '运行中';
                autoTestStatusEl.className = 'ms-2 badge bg-success';
                startAutoTestBtn.disabled = true;
                stopAutoTestBtn.disabled = false;
                
                // 每2秒调用一次
                autoTestInterval = setInterval(() => {
                    testCircuitBreaker();
                }, 2000);
            }
            
            // 停止自动测试
            function stopAutoTest() {
                if (!autoTestInterval) {
                    return;
                }
                
                clearInterval(autoTestInterval);
                autoTestInterval = null;
                
                autoTestStatusEl.textContent = '已停止';
                autoTestStatusEl.className = 'ms-2 badge bg-secondary';
                startAutoTestBtn.disabled = false;
                stopAutoTestBtn.disabled = true;
            }
            
            // 事件监听
            testCircuitBreakerBtn.addEventListener('click', testCircuitBreaker);
            startAutoTestBtn.addEventListener('click', startAutoTest);
            stopAutoTestBtn.addEventListener('click', stopAutoTest);
            
            // 熔断器状态监控
            const refreshStatusBtn = document.getElementById('refreshStatusBtn');
            const circuitBreakerState = document.getElementById('circuitBreakerState');
            const failureRate = document.getElementById('failureRate');
            const slowCallRate = document.getElementById('slowCallRate');
            const successfulCalls = document.getElementById('successfulCalls');
            const failedCalls = document.getElementById('failedCalls');
            const slowCalls = document.getElementById('slowCalls');
            const notPermittedCalls = document.getElementById('notPermittedCalls');
            
            // 刷新熔断器状态
            function refreshCircuitBreakerStatus() {
                fetch('/api/circuit-breaker-status/serviceInfoCircuitBreaker')
                    .then(response => response.json())
                    .then(data => {
                        if (data.error) {
                            console.error('获取熔断器状态失败:', data.error);
                            circuitBreakerState.textContent = '错误';
                            circuitBreakerState.className = 'ms-2 badge bg-danger';
                            return;
                        }
                        
                        const details = data.details;
                        
                        // 更新状态
                        circuitBreakerState.textContent = details.state;
                        switch (details.state) {
                            case 'CLOSED':
                                circuitBreakerState.className = 'ms-2 badge bg-success';
                                break;
                            case 'OPEN':
                                circuitBreakerState.className = 'ms-2 badge bg-danger';
                                break;
                            case 'HALF_OPEN':
                                circuitBreakerState.className = 'ms-2 badge bg-warning';
                                break;
                            default:
                                circuitBreakerState.className = 'ms-2 badge bg-secondary';
                        }
                        
                        // 更新指标
                        failureRate.textContent = details.failureRate;
                        slowCallRate.textContent = details.slowCallRate;
                        successfulCalls.textContent = details.numberOfSuccessfulCalls;
                        failedCalls.textContent = details.numberOfFailedCalls;
                        slowCalls.textContent = details.numberOfSlowCalls;
                        notPermittedCalls.textContent = details.numberOfNotPermittedCalls;
                    })
                    .catch(error => {
                        console.error('获取熔断器状态出错:', error);
                        circuitBreakerState.textContent = '错误';
                        circuitBreakerState.className = 'ms-2 badge bg-danger';
                    });
            }
            
            // 初始刷新和定时刷新
            refreshCircuitBreakerStatus();
            const statusRefreshInterval = setInterval(refreshCircuitBreakerStatus, 5000); // 每5秒刷新一次
            
            // 手动刷新按钮
            refreshStatusBtn.addEventListener('click', refreshCircuitBreakerStatus);
        });
    </script>
</body>
</html> 